/*
Copyright (C) 1996-1997 Id Software, Inc.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/
// r_misc.c

#include "quakedef.h"



/*
==================
R_InitTextures
==================
*/
void	R_InitTextures (void)
{
    int		x,y, m;
    byte	*dest;

// create a simple checkerboard texture for the default
    r_notexture_mip = Hunk_AllocName (sizeof(texture_t) + 16*16+8*8+4*4+2*2, "notexture");

    r_notexture_mip->width = r_notexture_mip->height = 16;
    r_notexture_mip->offsets[0] = sizeof(texture_t);
    r_notexture_mip->offsets[1] = r_notexture_mip->offsets[0] + 16*16;
    r_notexture_mip->offsets[2] = r_notexture_mip->offsets[1] + 8*8;
    r_notexture_mip->offsets[3] = r_notexture_mip->offsets[2] + 4*4;

    for (m=0 ; m<4 ; m++)
    {
        dest = (byte *)r_notexture_mip + r_notexture_mip->offsets[m];
        for (y=0 ; y< (16>>m) ; y++)
            for (x=0 ; x< (16>>m) ; x++)
            {
                if (  (y< (8>>m) ) ^ (x< (8>>m) ) )
                    *dest++ = 0;
                else
                    *dest++ = 0xff;
            }
    }
}

byte	dottexture[8][8] =
{
    {0,1,1,0,0,0,0,0},
    {1,1,1,1,0,0,0,0},
    {1,1,1,1,0,0,0,0},
    {0,1,1,0,0,0,0,0},
    {0,0,0,0,0,0,0,0},
    {0,0,0,0,0,0,0,0},
    {0,0,0,0,0,0,0,0},
    {0,0,0,0,0,0,0,0},
};

void R_InitParticleTexture (void)
{
    int		x,y;
    byte	data[8][8][4];

    //
    // particle texture
    //
    particletexture = texture_extension_number++;
    GL_Bind(particletexture);

    for (x=0 ; x<8 ; x++)
    {
        for (y=0 ; y<8 ; y++)
        {
            data[y][x][0] = 255;
            data[y][x][1] = 255;
            data[y][x][2] = 255;
            data[y][x][3] = dottexture[x][y]*255;
        }
    }
    glTexImage2D (GL_TEXTURE_2D, 0, gl_alpha_format, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}

/*
==================
PENTA:
Loads textures needed for the fluid shaders
==================
*/
void R_InitShaderTexture(void)
{
    newwatertex = Image_LoadShader("penta/q3water.tga");
    newslimetex = Image_LoadShader("penta/q3slime.tga");
    newlavatex = Image_LoadShader("penta/q3lava.tga");
    newteletex = Image_LoadShader("penta/q3tele.tga");
    newenvmap = Image_LoadShader("penta/q3envmap.tga");
    newhalotex = Image_LoadShader("penta/utflare5.tga");
}

/*
===============
R_Envmap_f

Grab six views for environment mapping tests
===============
*/
void R_Envmap_f (void)
{
    byte	buffer[256*256*4];

    glDrawBuffer  (GL_FRONT);
    glReadBuffer  (GL_FRONT);
    envmap = true;

    r_refdef.vrect.x = 0;
    r_refdef.vrect.y = 0;
    r_refdef.vrect.width = 256;
    r_refdef.vrect.height = 256;

    r_refdef.viewangles[0] = 0;
    r_refdef.viewangles[1] = 0;
    r_refdef.viewangles[2] = 0;
    GL_BeginRendering (&glx, &gly, &glwidth, &glheight);
    R_RenderView ();
    glReadPixels (0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
    COM_WriteFile ("env0.rgb", buffer, sizeof(buffer));

    r_refdef.viewangles[1] = 90;
    GL_BeginRendering (&glx, &gly, &glwidth, &glheight);
    R_RenderView ();
    glReadPixels (0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
    COM_WriteFile ("env1.rgb", buffer, sizeof(buffer));

    r_refdef.viewangles[1] = 180;
    GL_BeginRendering (&glx, &gly, &glwidth, &glheight);
    R_RenderView ();
    glReadPixels (0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
    COM_WriteFile ("env2.rgb", buffer, sizeof(buffer));

    r_refdef.viewangles[1] = 270;
    GL_BeginRendering (&glx, &gly, &glwidth, &glheight);
    R_RenderView ();
    glReadPixels (0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
    COM_WriteFile ("env3.rgb", buffer, sizeof(buffer));

    r_refdef.viewangles[0] = -90;
    r_refdef.viewangles[1] = 0;
    GL_BeginRendering (&glx, &gly, &glwidth, &glheight);
    R_RenderView ();
    glReadPixels (0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
    COM_WriteFile ("env4.rgb", buffer, sizeof(buffer));

    r_refdef.viewangles[0] = 90;
    r_refdef.viewangles[1] = 0;
    GL_BeginRendering (&glx, &gly, &glwidth, &glheight);
    R_RenderView ();
    glReadPixels (0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
    COM_WriteFile ("env5.rgb", buffer, sizeof(buffer));

    envmap = false;
    glDrawBuffer  (GL_BACK);
    glReadBuffer  (GL_BACK);
    GL_EndRendering ();
}

/*
===============
R_Init
===============
*/
void R_Init (void)
{
    extern cvar_t gl_finish;

    Cmd_AddCommand ("timerefresh", R_TimeRefresh_f);
    Cmd_AddCommand ("envmap", R_Envmap_f);
    Cmd_AddCommand ("pointfile", R_ReadPointFile_f);

    Cvar_RegisterVariable (&r_norefresh, NULL);
    Cvar_RegisterVariable (&r_lightmap, NULL);
    Cvar_RegisterVariable (&r_fullbright, NULL);
    Cvar_RegisterVariable (&r_drawentities, NULL);
    Cvar_RegisterVariable (&r_drawviewmodel, NULL);
    Cvar_RegisterVariable (&r_shadows, NULL);
    Cvar_RegisterVariable (&r_wateralpha, NULL);
    Cvar_RegisterVariable (&r_waterwarp, NULL);
    Cvar_RegisterVariable (&r_dynamic, NULL);
    Cvar_RegisterVariable (&r_novis, NULL);

    Cvar_RegisterVariable (&gl_finish, NULL);
    Cvar_RegisterVariable (&gl_clear, NULL);
    Cvar_RegisterVariable (&gl_cull, NULL);
    Cvar_RegisterVariable (&gl_smoothmodels, NULL);
    Cvar_RegisterVariable (&gl_affinemodels, NULL);
    Cvar_RegisterVariable (&gl_polyblend, NULL);
    Cvar_RegisterVariable (&gl_flashblend, NULL);
    Cvar_RegisterVariable (&gl_playermip, NULL);
    Cvar_RegisterVariable (&gl_nocolors, NULL);
    Cvar_RegisterVariable (&gl_reporttjunctions, NULL);
    Cvar_RegisterVariable (&gl_doubleeyes, NULL);
    Cvar_RegisterVariable (&gl_watershader, NULL);//PENTA: register our vars.
    Cvar_RegisterVariable (&gl_calcdepth, NULL);

    Cvar_RegisterVariable (&sh_lightmapbright, NULL);
    Cvar_RegisterVariable (&sh_radiusscale, NULL);
    Cvar_RegisterVariable (&sh_visiblevolumes, NULL);
    Cvar_RegisterVariable (&sh_entityshadows, NULL);
    Cvar_RegisterVariable (&sh_worldshadows, NULL);
    Cvar_RegisterVariable (&sh_showlightnum, NULL);
    Cvar_RegisterVariable (&sh_glows, NULL);
    Cvar_RegisterVariable (&sh_fps, NULL); // muff
    Cvar_RegisterVariable (&sh_debuginfo, NULL);
    Cvar_RegisterVariable (&sh_norevis, NULL);
    Cvar_RegisterVariable (&sh_nosvbsp, NULL);
    Cvar_RegisterVariable (&sh_noeclip, NULL);
    Cvar_RegisterVariable (&sh_infinitevolumes, NULL);
    Cvar_RegisterVariable (&sh_noscissor, NULL);
    Cvar_RegisterVariable (&sh_nocleversave, NULL);
    Cvar_RegisterVariable (&sh_bumpmaps, NULL);
    Cvar_RegisterVariable (&sh_colormaps, NULL);
    Cvar_RegisterVariable (&sh_playershadow, NULL);
    Cvar_RegisterVariable (&sh_nocache, NULL);
    Cvar_RegisterVariable (&sh_glares, NULL);
    Cvar_RegisterVariable (&sh_noefrags, NULL);
    Cvar_RegisterVariable (&sh_showtangent, NULL);
    Cvar_RegisterVariable (&sh_noshadowpopping, NULL);

    Cvar_RegisterVariable (&mir_detail, NULL);
    Cvar_RegisterVariable (&mir_frameskip, NULL);
    Cvar_RegisterVariable (&mir_distance, NULL);

    Cvar_RegisterVariable (&fog_r, NULL);
    Cvar_RegisterVariable (&fog_g, NULL);
    Cvar_RegisterVariable (&fog_b, NULL);
    Cvar_RegisterVariable (&fog_start, NULL);
    Cvar_RegisterVariable (&fog_end, NULL);
    Cvar_RegisterVariable (&fog_enabled, NULL);
    Cvar_RegisterVariable (&fog_waterfog, NULL);

    Cvar_RegisterVariable (&gl_overbright, NULL);
    Cvar_RegisterVariable (&gl_wireframe, NULL);
    Cvar_RegisterVariable (&gl_truform, NULL);
    Cvar_RegisterVariable (&gl_truform_tesselation, NULL);
    Cvar_RegisterVariable (&gl_transformlerp, NULL);

    R_InitParticleEffects();
    R_InitParticles ();
    R_InitDecals ();
    R_InitParticleTexture ();
    R_InitShaderTexture();

#ifdef GLTEST
    Test_Init ();
#endif

    playertextures = texture_extension_number;
    texture_extension_number += 16;
}

/*
===============
R_TranslatePlayerSkin

Translates a skin texture by the per-player color lookup
===============
*/
void R_TranslatePlayerSkin (int playernum)
{
    int		        top, bottom;
    byte		    translate[256];
    unsigned	    translate32[256];
    int		        i, j, s;
    model_t		    *model;
    alias3data_t 	*data;
    aliashdr_t 	    *paliashdr;
    byte		    *original;
    static unsigned	pixels[512*256], *out;		// <AWE> added "static" otherwise array has to be <32kb.
    unsigned	    scaled_width, scaled_height;
    int		        inwidth, inheight;
    byte		    *inrow;
    unsigned	    frac, fracstep;

    GL_DisableMultitexture();

    top = cl.scores[playernum].colors & 0xf0;
    bottom = (cl.scores[playernum].colors & 15) << 4;

    for (i=0 ; i<256 ; i++)
    {
        translate[i] = i;
    }

    for (i=0 ; i<16 ; i++)
    {
		// the artists made some backward ranges. sigh.
		translate[TOP_RANGE + i] = (top < 128) ? top + i : top + 15 - i;
		translate[BOTTOM_RANGE + i] = (bottom < 128) ? bottom + i : bottom + 15 - i;
    }

    //
    // locate the original skin pixels
    //
    currententity = &cl_entities[1 + playernum];
    model = currententity->model;

    if (!model) return;		                // player doesn't have a model yet
    if (model->type != mod_alias) return;   // only translate skins on alias models

    // HACK HACK HACK -> garanted to work with original player model _ONLY_
    data = (alias3data_t *)Mod_Extradata (model);
    paliashdr = (aliashdr_t *)((char*)data + data->ofsSurfaces[0]);
    s = paliashdr->skinwidth * paliashdr->skinheight;

    if (currententity->skinnum < 0 || currententity->skinnum >= paliashdr->numskins)
    {
        Con_Printf("(%d): Invalid player skin #%d\n", playernum, currententity->skinnum);
        original = (byte *)paliashdr + paliashdr->texels[0];
    }
    else
    {
        original = (byte *)paliashdr + paliashdr->texels[currententity->skinnum];
    }

    if (s & 3)
    {
        Sys_Error ("R_TranslateSkin: s&3");
    }
    inwidth = paliashdr->skinwidth;
    inheight = paliashdr->skinheight;

    // because this happens during gameplay, do it fast
    // instead of sending it through gl_upload 8
    GL_Bind(playertextures + playernum);

    scaled_width = gl_max_size.value < 512 ? gl_max_size.value : 512;
    scaled_height = gl_max_size.value < 256 ? gl_max_size.value : 256;

    // allow users to crunch sizes down even more if they want
	scaled_width >>= max(1, (int)gl_playermip.value);
	scaled_height >>= max(1, (int)gl_playermip.value);

    if (VID_Is8bit() && gl_palettedtex)   // 8bit texture upload <AWE> requires EXT_paletted_texture
    {
        byte *out2;

        out2 = (byte *)pixels;
        memset(pixels, 0, sizeof(pixels));
        fracstep = inwidth*0x10000/scaled_width;

        for (i=0 ; i<(int) scaled_height ; i++, out2 += scaled_width)
        {
            inrow = original + inwidth*(i*inheight/scaled_height);
            frac = fracstep >> 1;

            for (j=0 ; j<(int) scaled_width ; j+=4)
            {
                out2[j] = translate[inrow[frac>>16]];
                frac += fracstep;
                out2[j+1] = translate[inrow[frac>>16]];
                frac += fracstep;
                out2[j+2] = translate[inrow[frac>>16]];
                frac += fracstep;
                out2[j+3] = translate[inrow[frac>>16]];
                frac += fracstep;
            }
        }
        GL_UploadColor8 ((byte *)pixels, scaled_width, scaled_height, TEX_NONE);
        return;
    }

    for (i=0 ; i<256 ; i++)
    {
        translate32[i] = d_8to24table[translate[i]];
    }
    out = pixels;

    fracstep = inwidth*0x10000/scaled_width;

    for (i=0 ; i<(int) scaled_height ; i++, out += scaled_width)
    {
        inrow = original + inwidth*(i*inheight/scaled_height);
        frac = fracstep >> 1;

        for (j=0 ; j<(int) scaled_width ; j+=4)
        {
            out[j] = translate32[inrow[frac>>16]];
            frac += fracstep;
            out[j+1] = translate32[inrow[frac>>16]];
            frac += fracstep;
            out[j+2] = translate32[inrow[frac>>16]];
            frac += fracstep;
            out[j+3] = translate32[inrow[frac>>16]];
            frac += fracstep;
        }
    }
    glTexImage2D (GL_TEXTURE_2D, 0, gl_solid_format, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);

    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}


/*
===============
R_NewMap
===============
*/
void R_NewMap (void)
{
    int		i;

    for (i=0 ; i<256 ; i++)
    {
        d_lightstylevalue[i] = 264;		// normal light value
    }
    memset (&r_worldentity, 0, sizeof(r_worldentity));
    r_worldentity.model = cl.worldmodel;

// clear out efrags in case the level hasn't been reloaded
// FIXME: is this one short?
    for (i=0 ; i<cl.worldmodel->numleafs ; i++)
    {
        cl.worldmodel->leafs[i].efrags = NULL;
    }
    r_viewleaf = NULL;
    R_ClearParticles ();
    R_ClearDecals ();

    GL_BuildLightmaps ();

    R_CopyVerticesToHunk();

    // identify sky texture
    skytexturenum = -1;

    for (i=0 ; i<cl.worldmodel->numtextures ; i++)
    {
        if (!cl.worldmodel->textures[i]) continue;
        if (ISSKYTEX(cl.worldmodel->textures[i]->name))
        {
            skytexturenum = i;
        }
        cl.worldmodel->textures[i]->texturechain = NULL;
    }
    strcpy(skybox_name, "default");
    skybox_cloudspeed = 0.1f;

    // PENTA: Delete all static lights
    numStaticShadowLights = 0;
    numShadowLights = 0;

    R_ClearInstantCaches();
    R_ClearBrushInstantCaches();
    R_NewMirrorChains();
}


/*
====================
R_TimeRefresh_f

For program optimization
====================
*/
void R_TimeRefresh_f (void)
{
    int		i;
    float		start, stop, time;

    glDrawBuffer  (GL_FRONT);
    glFinish ();

    start = Sys_FloatTime ();
    for (i=0 ; i<128 ; i++)
    {
        r_refdef.viewangles[1] = i/128.0*360.0;
        R_RenderView ();
    }

    glFinish ();
    stop = Sys_FloatTime ();
    time = stop-start;
    Con_Printf ("%f seconds (%f fps)\n", time, 128/time);

    glDrawBuffer  (GL_BACK);
    GL_EndRendering ();
}
